import {
  Mesh, Curve, InstancedMesh, Object3D, Group,
  BufferGeometry, InstancedBufferGeometry,
  Float32BufferAttribute, InstancedBufferAttribute, Uint16BufferAttribute,
  Matrix4, Vector3, Vector2, Matrix3, Quaternion, Euler,
  MeshBasicMaterial,
  LineSegments,
  Color,
  LineBasicMaterial,
  ShapeUtils,
} from "three";
import {mergeGeometries} from "three/examples/jsm/utils/BufferGeometryUtils.js";
import {GLTFExporter} from "three/examples/jsm/exporters/GLTFExporter.js";


const gltfExporter = new GLTFExporter();
const link = document.createElement( 'a' );
function save( blob, filename ) {

  if ( link.href ) {

    URL.revokeObjectURL( link.href );

  }

  link.href = URL.createObjectURL( blob );
  link.download = filename || 'data.json';
  link.dispatchEvent( new MouseEvent( 'click' ) );

}

export function exportGLB(scene:Object3D,filename) {
if(gltfExporter){

  const optimizedAnimations = [];
  
  gltfExporter.parse( scene, function ( result:ArrayBuffer ) {
    
    // saveArrayBuffer( result, 'scene.glb' );
    save( new Blob( [ result ], { type: 'application/octet-stream' } ), filename + '_' +Date.now()+'.glb')
    
  }, undefined, { binary: true, animations: optimizedAnimations } );
}
}
const axisMap = {
  x: new Vector3(1, 0, 0),
  y: new Vector3(0, 1, 0),
  z: new Vector3(0, 0, 1),
};

// 完全可以改成只计算顶点， 这样多个箭头就可以直接合并了。 如果用合并工具， 虚线也不能说废了吧，但是没必要
// 现在用索引了，不能计算距离了，虚线真的废了，应该没有需要虚线的吧
export class ArrowLineGeometry extends BufferGeometry {
  type = "ArrowLineGeometry";
  constructor(h = 1, w = 1, dir = "x",reverse = false,color = 0xffffff) {
    super();
    const colorObj = new Color(color);
    const colorArr = colorObj.toArray();
    const colors = [];
    for (let i = 0; i < 4; i++) {
      colors.push(...colorArr);
    }
    const x = w * 0.5;
    const points = [0, 0, 0];// 顺序是 原点出发 到最远点，再画箭头两翼
    const indexes = reverse? [0,1,0,2,0,3]:[0,1, 1,2, 1,3];
    switch (dir) {
      case "x": //xzo平面
      if(reverse) points.push(h, 0, 0,  x, 0, x,  x, 0, -x)
      else points.push(h, 0, 0,h - x, 0, x, h - x, 0, -x);
        break;
      case "y": // xyo平面
      if(reverse) points.push(0, h, 0,   x, h - x, 0,  -x, h - x, 0)
      else points.push(0, h, 0,  x, h - x, 0,  -x, h - x, 0);
        break;
      case "z": // zyo平面
      if(reverse) points.push(0, 0, h,  0, x, h - x,  0, -x, h - x)
      else points.push(0, 0, h,  0, x, h - x,  0, -x, h - x);
        break;
      default:
        break;
    }
    this.setIndex(new Uint16BufferAttribute(indexes, 1));
    this.setAttribute("position", new Float32BufferAttribute(points, 3));
    this.setAttribute("color", new Float32BufferAttribute(colors, 3));
  }
}

export function getArowAixs(size = 10, width = 2,) {
  const mat = new LineBasicMaterial({ vertexColors: true });
  const arrowLineX = new ArrowLineGeometry(size, width, "x",false,0xff0000);
  const arrowLineY = new ArrowLineGeometry(size, width, "y",false,0x00ff00);
  const arrowLineZ = new ArrowLineGeometry(size, width, "z",false,0x0000ff);
  const geo = mergeGeometries([arrowLineX, arrowLineY, arrowLineZ]);
  return new LineSegments(geo, mat);
}

/**
 *  原LatheGeometry 增加了螺旋长度 ，迭代次数，解除了角度范围限制
 * 现在用圆环车出来的几何体，肯定是一半正面一半反面的。
 */
export class SpiralGeometry extends BufferGeometry {
  type = "SpiralGeometry";
  parameters: { points: Vector2[]; segments: number; phiStart: number; phiLength: number; };
  constructor(
    points = [new Vector2(0, -0.5), new Vector2(0.5, 0), new Vector2(0, 0.5)],
    segments = 12,
    phiStart = 0,
    phiLength = Math.PI * 2,
    spiralOffset = 0,
    repeat = 1
  ) {
    // 进行一次车削偏移的距离，重复车削的次数
    // 重复车削这里，理论上是可以减少计算的，直接使用第一次车削的，在轴向上偏移和旋转即可。   不过我发现这个和直接走原本的逻辑也没差多少。

    // 直接走原本的逻辑算了 只需要加这么一行代码,不好意思，得两行
    phiLength *= repeat;
    segments *= repeat; //
    super();


    this.parameters = {
      points: points,
      segments: segments,
      phiStart: phiStart,
      phiLength: phiLength,
    };

    segments = Math.floor(segments);

    // buffers

    const indices = [];
    const vertices = [];
    const uvs = [];
    const initNormals = [];
    const normals = [];

    // helper variables

    const inverseSegments = 1.0 / segments;
    const vertex = new Vector3();
    const uv = new Vector2();
    const normal = new Vector3();
    const curNormal = new Vector3();
    const prevNormal = new Vector3();
    let dx = 0;
    let dy = 0;

    // pre-compute normals for initial "meridian"

    for (let j = 0; j <= points.length - 1; j++) {
      switch (j) {
        case 0: // special handling for 1st vertex on path
          dx = points[j + 1].x - points[j].x;
          dy = points[j + 1].y - points[j].y;

          normal.x = dy * 1.0;
          normal.y = -dx;
          normal.z = dy * 0.0;

          prevNormal.copy(normal);

          normal.normalize();

          initNormals.push(normal.x, normal.y, normal.z);

          break;

        case points.length - 1: // special handling for last Vertex on path
          initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z);

          break;

        default: // default handling for all vertices in between
          dx = points[j + 1].x - points[j].x;
          dy = points[j + 1].y - points[j].y;

          normal.x = dy * 1.0;
          normal.y = -dx;
          normal.z = dy * 0.0;

          curNormal.copy(normal);

          normal.x += prevNormal.x;
          normal.y += prevNormal.y;
          normal.z += prevNormal.z;

          normal.normalize();

          initNormals.push(normal.x, normal.y, normal.z);

          prevNormal.copy(curNormal);
      }
    }

    // generate vertices, uvs and normals
    const yOffset = (spiralOffset / phiLength) * repeat; // 车削的偏移量， 每次车削的偏移量， 车削的长度
    for (let i = 0; i <= segments; i++) {
      const deltaPhi = i * inverseSegments * phiLength;
      const phi = phiStart + deltaPhi;

      const sin = Math.sin(phi);
      const cos = Math.cos(phi);

      for (let j = 0; j <= points.length - 1; j++) {
        // vertex

        vertex.x = points[j].x * sin;
        vertex.y = points[j].y + deltaPhi * yOffset;
        vertex.z = points[j].x * cos;

        vertices.push(vertex.x, vertex.y, vertex.z);

        // uv

        uv.x = i / segments;
        uv.y = j / (points.length - 1);

        uvs.push(uv.x, uv.y);

        // normal 我加了偏移之后，法线应该也发生了变化，暂时还不知道怎么改。

        const x = initNormals[3 * j + 0] * sin;
        const y = initNormals[3 * j + 1];
        const z = initNormals[3 * j + 0] * cos;

        normals.push(x, y, z);
      }
    }

    // indices

    for (let i = 0; i < segments; i++) {
      for (let j = 0; j < points.length - 1; j++) {
        const base = j + i * points.length;

        const a = base;
        const b = base + points.length;
        const c = base + points.length + 1;
        const d = base + 1;

        // faces

        indices.push(a, b, d);
        indices.push(c, d, b);
      }
    }

    //  实现迭代 repeat参数，
    /*   if(repeat > 1){ 
      const vertices1 = [...vertices] ;
      vertices.slice(points.length * 3) //  要去掉车削开始的点，
      for (let index = 0; index < repeat; index++) {
        //  把所有的点都绕 Y轴旋转 phiLength ,并位移 spiral Offset 好像还不如直接把上面的逻辑 再执行N 次 这里恐怕有误差
        for (let j = 0; j < vertices.length; j+=3) {
          const element = array[j];
          
        }
      }
    }
 */
    // build geometry

    this.setIndex(indices);
    this.setAttribute("position", new Float32BufferAttribute(vertices, 3));
    this.setAttribute("uv", new Float32BufferAttribute(uvs, 2));
    this.setAttribute("normal", new Float32BufferAttribute(normals, 3));
  }

  copy(source) {
    super.copy(source);

    this.parameters = Object.assign({}, source.parameters);

    return this;
  }

  static fromJSON(data) {
    return new SpiralGeometry(
      data.points,
      data.segments,
      data.phiStart,
      data.phiLength
    );
  }
}
/**
 * 传入一个几何体，和点，使用instance 实例化多个几何体，。
 * @param {BufferGeometry} geo
 * @param {Vector3[]} points
 */
function instanceInPoints(geo: BufferGeometry, points: Vector3[]) {
  const instanceGeometry = new InstancedBufferGeometry();
  instanceGeometry.attributes = geo.attributes;
  instanceGeometry.index = geo.index;
  return instanceGeometry;
}

/**
 * 实例化一个网格，在多个点上
 * @param {Mesh} mesh
 * @param {Vector3[]} points
 * @returns
 */
function instanceMeshOnPoints(mesh: Mesh, points: Vector3[]) {
  const instancedMesh = new InstancedMesh(
    mesh.geometry,
    mesh.material,
    points.length
  );
  const matrix4 = new Matrix4();
  for (let index = 0; index < points.length; index++) {
// @ts-ignore
    matrix4.setPosition(...points[index]);
    instancedMesh.setMatrixAt(index, matrix4);
  }
  return instancedMesh;
}
/**
 * 遍历对象中全部mesh，实例化在多个点上
 * @param {Object3D} group
 * @param {Vector3[]} points
 * @returns
 */
export function instanceObject3DOnPoints(group: Object3D, points: Vector3[]) {
  const g = new Group();
  group.updateMatrixWorld();
  if (!(group as Mesh).isMesh) {
    group.matrixWorld.decompose(g.position, g.quaternion, g.scale);
  }


  // 我想让大家共用一个matrix
  let matrixArray = [],instanceCount = 0;
  // 可以直接传入一个matrix 数组
// @ts-ignore
  if (points[0].isMatrix4) {
    instanceCount = points.length;
    for (let index = 0; index < points.length; index++) {
// @ts-ignore
      matrixArray.push(...points[index].elements);
    }
  } else if(points[0].isVector3) {
    instanceCount = points.length;

    const matrix4 = new Matrix4();

    for (let index = 0; index < points.length; index++) {
// @ts-ignore

      matrix4.setPosition(...points[index]);
      matrixArray.push(...matrix4.elements);
    }
  }else if(typeof  points[0]  ==='number'){
    matrixArray = points;
    instanceCount = points.length /16;

  }
  group.traverse((mesh:Mesh) => {
    if (mesh.isMesh) {
      const instancedMesh = new InstancedMesh(
        mesh.geometry,
        mesh.material,
        instanceCount
      );
      mesh.matrixWorld.decompose(mesh.position, mesh.quaternion, mesh.scale);
      g.add(instancedMesh);
    }
  });
  const matrixTypeAttr = new Float32Array(matrixArray);
  const matrixAttr = new InstancedBufferAttribute(matrixTypeAttr, 16);
  g.children.forEach(
    (mesh:InstancedMesh) => {
      mesh.instanceMatrix = matrixAttr;
    }
  );
  g.userData.matrixAttr = matrixAttr;

  return g;
}


export function getInstancesMatrixAlongCurve(curve: Curve<any>, count: number, axis = "z",offset = 0) {
  const geo = new BufferGeometry();
  const matrix4 = new Matrix4();
  const segments = count -1;
  const points1  = [];
  const tangents =[];
  let factor = 0,o = offset%1;
  for (let index = 0; index <= segments; index++) {
    // 不是闭合曲线这样会损失末端的点
    factor = (index / segments + offset)%1;
    if(factor <0) factor +=1;
    if(factor >1) factor -=1;
    const tangent2 = curve.getTangentAt(factor);
    const pos = curve.getPointAt(factor);
    tangents.push(tangent2);
    points1.push(pos);
  }
  const baseAxis = axisMap[axis];
  const matrixArray = [];
  const q = new Quaternion(),p = new Vector3(),scale = new Vector3(1,1,1),tangent = new Vector3();
  for (let index = 0; index < points1.length; index++) {
// @ts-ignore
    // 兼容2d曲线   但是这个旋转，不能完全兼容，2d还是特殊处理方便一些
    p.set(...points1[index]);tangent.set(...tangents[index]);
    q.setFromUnitVectors(baseAxis, tangent);
    matrix4.compose(p, q, scale);
    matrixArray.push(...matrix4.elements);
  }
  return matrixArray;
}


/**
 * 实例化于曲线
 * @param {Curve} curve
 * @param {number} count
 * @param {Object3D} group
 */
export function instanceAlongCurve(curve: Curve<any>, count: number, group: Object3D, axis = "z") {
  const matrixArray = getInstancesMatrixAlongCurve(curve, count,  axis);
  const g = instanceObject3DOnPoints(group, matrixArray);
  return g;
}
/**
 * 实例曲线偏移
 */
export function instanceMoveALongCurve(curve: Curve<any>, segments: number, group: Object3D, axis = "z",offset = 0) {
  const points1:Vector3[] = curve.getSpacedPoints(segments);
  const tangents =[],matrix4 = new Matrix4();
  for (let index = 0; index <= segments; index++) {
    const tangent2 = curve.getTangent(index / segments);
    tangents.push(tangent2);
  }
  const baseAxis = axisMap[axis];
  const matrixArray = [];
  const q = new Quaternion(),p = new Vector3(),scale = new Vector3(1,1,1),tangent = new Vector3();
  for (let index = 0; index < points1.length; index++) {
// @ts-ignore
    // 兼容2d曲线   但是这个旋转，不能完全兼容，2d还是特殊处理方便一些
    p.set(...points1[index]);tangent.set(...tangents[index]);
    q.setFromUnitVectors(baseAxis, tangent);
    matrix4.compose(p, q, scale);
    matrixArray.push(...matrix4.elements);
  }
}
/**
 * 
 * curve1主路径，curve2在路径上实例化，那么对每个curve2 都要进行管道或者挤出那种运算
 * Create a BufferGeometry surface between two curves
 * @param {Curve} curve1 - First curve (base curve)
 * @param {Curve} profileCurve - Second curve (contour curve)
 * @param {{segments1:number, segments2:number, closed1:boolean, closed2:boolean}} options - Optional configuration
 * @param {number} [options.segments1=32] - Number of segments to sample along the curves
 * @param {number} [options.segments2=32] - Number of segments to sample along the curves
 * @param {boolean} [options.closed1=false] - Whether the curves form a closed loop
 * @param {boolean} [options.closed2=false] - Whether the curves form a closed loop
 * @param {boolean} [options.cover=false] - 封口
 * @returns {BufferGeometry} Generated geometry between the curves
 */
// @ts-ignore
export function curvesToMeshGeometry(curve, profileCurve: Curve, options: { segments1: number; segments2: number; closed1?: boolean; closed2?: boolean; cover?:boolean} = {}): BufferGeometry {
  const { segments1 = 32, segments2 = 32, closed1 = curve.closed, closed2 = profileCurve.closed,cover =false } = options;

  // Validate input curves
  if (typeof curve.getPoint !== "function" || typeof profileCurve.getPoint !== "function") {
    throw new Error("Input must be Three.js Curve instances");
  }
  // 默认轮廓曲线是在xy 平面的
  const mainPoints = curve.getSpacedPoints(segments1);// 这个方法获取到的是重采样后的点分布更均匀
  const profilePoints = profileCurve.getPoints(segments2);
  let radiusArr = new Array(segments1 + 1).fill(1);
  let skewArr = new Array(segments1 + 1).fill(0);// 曲线倾斜
  // for (let index = 0; index < skewArr.length; index++) {
  //   skewArr[index] = index / skewArr.length * Math.PI;
  // }
  if (typeof curve.getRadiusArray === "function") {
    radiusArr = curve.getRadiusArray(segments1);
  }
  if (typeof curve.getSkewArray === "function") {
    skewArr = curve.getSkewArray(segments1);
  }
  const frames = curve.computeFrenetFrames(segments1, closed1);//我自定义的折线和这个有点儿冲突
  const vertices = [];
  const indices = [];
  // 若要uv均匀，应该计算路径曲线的长度，和 轮廓曲线的长度，比值
  const pathLength = curve.getLength(segments1);
  const profileLength = profileCurve.getLength(segments2);
  /**@type {Vector3[]} */
  const tangents2: Vector3[] = [];
  for (let index = 0; index <= segments2; index++) {
    const tangent2 = profileCurve.getTangent(index / segments2,new Vector3());
    tangents2.push(tangent2);
  }
  // 假设路径曲线长于轮廓曲线  这里为了路径闭合的时候 贴图无缝，取整了
  const Uratio = Math.floor(pathLength / profileLength);
  const uvs = [],normals = [];
  const q1 = new Quaternion(),
    elur = new Euler(),
    v1 = new Vector3(),
    normal = new Vector3(),
    bnormal = new Vector3(),
    v4 = new Vector3();
  // 外循环路径曲线
  let basePoint = new Vector3();
  // 分段数为1 有两个点， 现在是 (segments1 + 1)*(segments2 + 1) 个点
  for (let i = 0; i <= segments1; i++) {
    //   内循环轮廓曲线，轮廓曲线只需要采样一次就可以了
    for (let j = 0; j <= segments2; j++) {
      basePoint.copy(mainPoints[i]);
      // 所有的点需要位移加旋转 我可以省事让three帮我做这个事？ 好像暂时管不了旋转了
      v1.copy(profilePoints[j]).multiplyScalar(radiusArr[i]);
      // 我好像知道为啥xy 倒过来了，正确的就应该是 -y x 颠倒之后就旋转了90度
      // 旋转这里应该是表现为normal和bnormal 绕着切向旋转 
      q1.setFromAxisAngle(frames.tangents[i], skewArr[i]);
      normal.copy(frames.normals[i]).applyQuaternion(q1).normalize().multiplyScalar(-v1.y);
      bnormal.copy(frames.binormals[i]).applyQuaternion(q1).normalize().multiplyScalar(v1.x);
      // 默认轮廓曲线是在xy 平面的 也就是默认基准轴是z轴
      basePoint.add(normal).add(bnormal);
      if (isNaN(basePoint.x) || isNaN(basePoint.y) || isNaN(basePoint.z)) { debugger }
      vertices.push(basePoint.x, basePoint.y, basePoint.z);
      // 计算uv   能保证uv 是均匀的 但是，如果路径曲线是封闭的，uv贴图不能无缝了
      // 总有一个无法无缝 
      uvs.push(i / segments1 * Uratio, j / segments2);
      // 且用两个切向叉乘计算法向
      // 考虑曲线倾斜
      // 曲线半径的变化，现在没法考虑到
      if( !curve.getRadiusArray ){
      v4.copy(tangents2[j]).applyQuaternion(q1);
      v4.crossVectors(frames.tangents[i],v4).normalize();
      normals.push(v4.x,v4.y,v4.z);
      }
    }
  }

  // Generate triangle indices
  let rowLen = segments2 + 1;
  for (let i = 0; i < segments1; i++) {
    for (let j = 0; j < segments2; j++) {
      // 这里无法直接知道绕匝方向 先假定这个四边面是 左上右下的
      let lt = i * rowLen + j,
        rt = lt + 1,
        lb = (i + 1) * rowLen + j,
        rb = lb + 1;
      // First triangle
      indices.push(lt, lb, rt);

      // Second triangle
      indices.push(rt, lb, rb);
    }
    // 如果轮廓曲线封闭 
    if (closed2) {
      let lt = i * rowLen + segments2 - 1,
        rt = i * rowLen,
        lb = (i + 1) * rowLen + segments2 - 1,
        rb = (i + 1) * rowLen;
      indices.push(lt, lb, rt);
      indices.push(rt, lb, rb);
    }
  }
  // 这里应该可以把内循环抽一个函数
  if (closed1) {
    let i = segments1-1;
    for (let j = 0; j < segments2 - 1; j++) {
      // 这里无法直接知道绕匝方向 先假定这个四边面是 左上右下的
      let lt = i * rowLen + j,
        rt = lt + 1,
        lb = j, // 0 * segments2 + j
        rb = lb + 1;
      // First triangle
      indices.push(lt, lb, rt);

      // Second triangle
      indices.push(rt, lb, rb);
    }
    // 如果轮廓曲线封闭 
    if (closed2) {
      let lt = i * rowLen + segments2 - 1,
        rt = i * rowLen,
        lb = (i + 1) * rowLen + segments2 - 1,
        rb = (i + 1) * rowLen;
      indices.push(lt, lb, rt);
      indices.push(rt, lb, rb);
    }
  }
  if( cover){
    const  indexOffset = (vertices.length/3  - profilePoints.length -1);
    let  shapeVertices = profilePoints.concat([])
    if ( ShapeUtils.isClockWise( shapeVertices ) === false ) {
      shapeVertices = shapeVertices.reverse();
    }


    const faces = ShapeUtils.triangulateShape(shapeVertices,[])
    // 其实我只需要这个index
    // 分别对应第一圈和最后一圈

    for ( let i = 0, l = faces.length; i < l; i ++ ) {

      const face = faces[ i ];

      const a = face[ 0 ] ;
      const b = face[ 1 ] ;
      const c = face[ 2 ] ;
      indices.push( a, b, c );
      indices.push( a+indexOffset, b+indexOffset, c+indexOffset );
    }


    
  }

  // Create BufferGeometry
  const geometry = new BufferGeometry();
  geometry.setAttribute("position", new Float32BufferAttribute(vertices, 3));
  geometry.setAttribute("uv", new Float32BufferAttribute(uvs, 2));
  geometry.setIndex(indices);
  if( !curve.getRadiusArray ){

    geometry.setAttribute("normal", new Float32BufferAttribute(normals, 3));
  }else{
    geometry.computeVertexNormals();
  }

  return geometry;
}

export class Stars3DGeometry extends BufferGeometry {
  /**
   *
   * @param {number} n 大于等于3
   * @param {number} innerRadius 内圆半径
   * @param {number} outerRadius 外圆半径
   * @param {number} depth 深度
   * @param {number} offset 内圆偏移
   * @param {number} outOffset 外圆偏移
   */
  constructor(n: number = 3, innerRadius: number = 1, outerRadius: number = 2, depth: number = 1, offset: number = 0, outOffset: number = 0) {
    super();
    // Define vertices and indices
    const vertices = [];
    const indices = [];
    const normals = [];
    const uvs = [];
    const { sin, cos, PI } = Math;
    const phiStart = PI * 0.5,
      step = (2 * PI) / n;
    let cosIn, sinIn, cosOut, sinOut;
    let inOutRadio = outerRadius / innerRadius;
    let phi = phiStart;
    // Generate vertices and indices
    for (let i = 0; i < n; i++) {
      phi += step;
      cosOut = cos(phi + outOffset);
      sinOut = sin(phi + outOffset);
      cosIn = cos(phi + step * 0.5 + offset);
      sinIn = sin(phi + step * 0.5 + offset);
      vertices.push(
        cosOut * outerRadius, sinOut * outerRadius, 0,
        cosIn * innerRadius, sinIn * innerRadius, 0
      );
      normals.push(cosOut, sinOut, 0, cosIn, sinIn, 0);
      // 这个uv 暂定为内圆为0-1，外圆缩放,即便内圆大于外圆，也只是缩小了，还是比较均匀分布的。
      uvs.push(cosOut * inOutRadio, sinOut * inOutRadio, cosIn, sinIn);
    }
    // 不加个点，就要手动解决最后一个点的问题
    // 把上下点放后面得了
    vertices.push(0, 0, depth, 0, 0, -depth);
    normals.push(0, 0, 1, 0, 0, -1);
    uvs.push(0, 0, 0, 0);

    // 索引 N*4个面
    let n1,
      n2,
      n3,
      z1 = 2 * n,
      z2 = z1 + 1;
    for (let i = 0; i < n; i++) {
      (n1 = 2 * i), (n2 = n1 + 1), (n3 = (n2 + 1) % z1);
      indices.push(
        z1, n1, n2, z2, n2, n1, // 背面得反着
        z1, n2, n3, z2, n3, n2
      );
    }
    this.index = new Uint16BufferAttribute(indices, 1);
    this.setAttribute("position", new Float32BufferAttribute(vertices, 3));
    this.setAttribute("normal", new Float32BufferAttribute(normals, 3));
    this.setAttribute("uv", new Float32BufferAttribute(uvs, 2));
  }
}


export class CurveHelper extends Object3D {
  /**
   *
   * @param {Curve} curve
   * @param {number} size
   */
  constructor(curve: Curve<any>, size: number = 5) {
    super()
    const { tangents, normals, binormals } = curve.computeFrenetFrames(32);

    const xArrowGeo = new ArrowLineGeometry(size, size * 0.5, 'x'),
      yArrowGeo = new ArrowLineGeometry(size, size * 0.5, 'y'),
      zArrowGeo = new ArrowLineGeometry(size, size * 0.5, 'z');
    const matR = new MeshBasicMaterial({ color: 'red' }),
      matG = new MeshBasicMaterial({ color: 'green' }),
      matB = new MeshBasicMaterial({ color: 'blue' });
    const len = tangents.length;
    const points = curve.getPoints(len - 1);
    const q1 = new Quaternion(), xAixs = new Vector3(1, 0, 0), yAixs = new Vector3(0, 1, 0), zAixs = new Vector3(0, 0, 1);
    for (let index = 0; index < len; index++) {
      const arrow = new LineSegments(xArrowGeo, matR);
      arrow.position.copy(points[index]);
      arrow.quaternion.setFromUnitVectors(xAixs, tangents[index]);

      const arrow2 = new LineSegments(yArrowGeo, matG);
      arrow2.position.copy(points[index]);
      arrow2.quaternion.setFromUnitVectors(yAixs, normals[index]);

      const arrow3 = new LineSegments(zArrowGeo, matB);
      arrow3.position.copy(points[index]);
      arrow3.quaternion.setFromUnitVectors(zAixs, binormals[index]);
      this.add(arrow, arrow2, arrow3);
    }

  }
}
// ... existing code ...

/**
 * 沿着曲线分布mesh (非实例化方式)
 * @param curve 曲线
 * @param mesh 要分布的mesh
 * @param count 分布数量
 * @param axis 初始朝向轴
 * @param group 可选，要添加到的组
 */
export function distributeAlongCurve(
  curve: Curve<any>, 
  mesh: Mesh, 
  count: number, 
  axis: keyof typeof axisMap = "z",
  group?: Group
): Group {
  const g = group || new Group();
  const segments = count - 1;
  const baseAxis = axisMap[axis];
  
  for (let i = 0; i <= segments; i++) {
    const t = i / segments;
    const position = curve.getPointAt(t);
    const tangent = curve.getTangentAt(t).normalize();
    
    // 创建mesh副本
    const meshCopy = mesh.clone();
    meshCopy.position.copy(position);
    
    // 设置朝向
    const quaternion = new Quaternion().setFromUnitVectors(baseAxis, tangent);
    meshCopy.quaternion.copy(quaternion);
    
    g.add(meshCopy);
  }
  
  return g;
}

/**
 * 沿着曲线运动
 * @param curve 曲线
 * @param axis 初始朝向轴
 * @param group 可选，要添加到的组
 * @param offset 偏移量
 */
export function moveAlongCurve(
  curve: Curve<any>, 
  axis: keyof typeof axisMap = "z",
  group?: Group,
  offset = 0

): Group {

  const g = group ;
  const count = group.children.length;
  const segments = count - 1;
  const baseAxis = axisMap[axis];
  
  for (let i = 0; i <= segments; i++) {
    const t = i / segments + offset;
    const position = curve.getPointAt(t);
    const tangent = curve.getTangentAt(t).normalize();
    
    // 创建mesh副本
    const meshCopy = g.children[i];
    meshCopy.position.copy(position);
    
    // 设置朝向
    const quaternion = new Quaternion().setFromUnitVectors(baseAxis, tangent);
    meshCopy.quaternion.copy(quaternion);  
  }
  
  return g;
}


export function moveAlongCurve2(
  curve: Curve<any>, 
  instancedMesh: InstancedMesh,
  axis: keyof typeof axisMap = "z",
  offset = 0
): void {
  const count = instancedMesh.count;
  const segments = count - 1;
  const baseAxis = axisMap[axis];
  const matrix = new Matrix4();
  const position = new Vector3();
  const quaternion = new Quaternion();
  const scale = new Vector3(1, 1, 1);
  
  for (let i = 0; i < count; i++) {
    const t = (i / segments + offset) % 1;
    curve.getPointAt(t, position);
    const tangent = curve.getTangentAt(t).normalize();
    
    // 设置朝向
    quaternion.setFromUnitVectors(baseAxis, tangent);
    
    // 更新矩阵
    matrix.compose(position, quaternion, scale);
    instancedMesh.setMatrixAt(i, matrix);
  }
  
  // 更新实例
  instancedMesh.instanceMatrix.needsUpdate = true;
}

// ... existing code ...
